package com.whfc.emp.manager.impl;

import com.alibaba.fastjson.JSONObject;
import com.whfc.base.enums.AppModuleType;
import com.whfc.base.service.WarnConfigService;
import com.whfc.common.constant.QueueConst;
import com.whfc.common.enums.AppWarnModuleType;
import com.whfc.common.enums.FenceType;
import com.whfc.common.exception.BizException;
import com.whfc.common.geometry.GeometryUtil;
import com.whfc.common.result.ResultEnum;
import com.whfc.common.util.DateUtil;
import com.whfc.common.util.MathUtil;
import com.whfc.emp.dao.*;
import com.whfc.emp.dto.*;
import com.whfc.emp.dto.openapi.OpenApiEmpWarnDTO;
import com.whfc.emp.entity.AppEmp;
import com.whfc.emp.entity.AppEmpWarn;
import com.whfc.emp.entity.AppEmpWarnRule;
import com.whfc.emp.enums.AppEmpWarnRuleType;
import com.whfc.emp.manager.AppEmpWarnRuleManager;
import com.whfc.entity.dto.msg.AppMsgToUserDTO;
import com.whfc.entity.dto.msg.PushMsgDTO;
import com.whfc.fuum.entity.SysEmpUser;
import com.whfc.fuum.service.SysUserService;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.dubbo.config.annotation.DubboReference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;

/**
 * @ClasssName AppEmpWarnRuleManagerImpl
 * @Description 人员报警服务类
 * @Author hw
 * @Date 2021/1/4 15:25
 * @Version 1.0
 */
@Service
public class AppEmpWarnRuleManagerImpl implements AppEmpWarnRuleManager {

    Logger logger = LoggerFactory.getLogger(this.getClass());
    private static final Integer ALARM_YES = 1;

    private static final String EMP_CONTENT = "%s在%s,%s,请您及时处理";

    @Autowired
    private AppEmpMapper appEmpMapper;

    @Autowired
    private AppEmpWarnRuleMapper appEmpWarnRuleMapper;

    @Autowired
    private AppEmpWarnMapper appEmpWarnMapper;

    @Autowired
    private AppEmpWarnRuleUserMapper appEmpWarnRuleUserMapper;

    @Autowired
    private AppEmpWarnRuleChannelMapper appEmpWarnRuleChannelMapper;

    @Autowired
    private AppEmpWarnRuleTimeMapper appEmpWarnRuleTimeMapper;

    @Autowired
    private AmqpTemplate amqpTemplate;

    @Autowired
    private AppEmpWarnRuleObjectMapper empWarnRuleObjectMapper;

    @DubboReference(interfaceClass = WarnConfigService.class, version = "1.0.0")
    private WarnConfigService warnConfigService;

    @DubboReference(interfaceClass = SysUserService.class, version = "1.0.0", timeout = 300000)
    private SysUserService sysUserService;

    @Override
    public void checkWarnRule(EmpWarnCheckDTO warnCheckDTO) {
        Integer alarmDoff = warnCheckDTO.getAlarmDoff();
        Integer alarmDrop = warnCheckDTO.getAlarmDrop();
        Integer alarmSos = warnCheckDTO.getAlarmSos();
        Integer alarmCrash = warnCheckDTO.getAlarmCrash();
        Integer alarmStill = warnCheckDTO.getAlarmStill();
        Integer leaveStill = warnCheckDTO.getLeaveStill();
        Integer fence = warnCheckDTO.getFence();

        List<Integer> ruleTypeList = new ArrayList<>();
        if (ALARM_YES.equals(alarmDoff)) {
            ruleTypeList.add(AppEmpWarnRuleType.DOFF.value());
        }
        if (ALARM_YES.equals(alarmDrop)) {
            ruleTypeList.add(AppEmpWarnRuleType.DROP.value());
        }
        if (ALARM_YES.equals(alarmSos)) {
            ruleTypeList.add(AppEmpWarnRuleType.SOS.value());
        }
        if (ALARM_YES.equals(alarmCrash)) {
            ruleTypeList.add(AppEmpWarnRuleType.CRASH.value());
        }
        if (ALARM_YES.equals(alarmStill)) {
            ruleTypeList.add(AppEmpWarnRuleType.STILL.value());
        }
        if (ALARM_YES.equals(leaveStill)) {
            ruleTypeList.add(AppEmpWarnRuleType.LEAVE.value());
        }
        if (ALARM_YES.equals(fence)) {
            ruleTypeList.add(AppEmpWarnRuleType.FENCE.value());
//            AppFenceDTO fenceDTO = empWarnRuleObjectMapper.selectByEmpId2(warnCheckDTO.getEmpId());
            AppFenceDTO fenceDTO = empWarnRuleObjectMapper.selectByEmpId2(1);
            if (fenceDTO == null) {
                return ;
            }
            warnCheckDTO.setRuleId(fenceDTO.getRuleId());
        }else {
            if (warnCheckDTO.getEmpId() != null) {
                // 检测是否触发电子围栏报警
                boolean isMeetWarnValue = this.checkFenceWarn(warnCheckDTO);
                if (isMeetWarnValue) {
                    ruleTypeList.add(AppEmpWarnRuleType.FENCE.value());
                }
            }
        }
        warnCheckDTO.setRuleTypeList(ruleTypeList);
        this.generateWarnAndMessage(warnCheckDTO);
    }


    @Override
    public void buildEmpWarnData(Integer ruleId, Date time,Double lnt,Double lat) {
        AppEmpWarnRule warnRule = appEmpWarnRuleMapper.selectByPrimaryKey(ruleId);
        if (warnRule == null) {
            throw new BizException(ResultEnum.FAILURE.getCode(), "该报警规则不存在。");
        }
        Integer deptId = warnRule.getDeptId();
        Integer ruleType = warnRule.getRuleType();

        // 查找报警人员
        List<AppWarnEmpDTO> objectIds = new ArrayList<>();
        List<AppEmpDTO> appEmpDTOS = appEmpMapper.selectEmpListByDeptId(deptId);
        for (AppEmpDTO appEmpDTO : appEmpDTOS) {
            AppWarnEmpDTO appWarnEmpDTO = new AppWarnEmpDTO();
            appWarnEmpDTO.setEmpId(appEmpDTO.getEmpId());
            appWarnEmpDTO.setEmpName(appEmpDTO.getEmpName());
            appWarnEmpDTO.setWorkTypeName(appEmpDTO.getWorkTypeName());
            objectIds.add(appWarnEmpDTO);
        }
        logger.info("报警人员:"+objectIds.size());
        if (objectIds == null || objectIds.size() == 0) {
            logger.info("没有报警人员");
            return;
        }
        // 查找报警通知方式
        List<Integer> channelList = appEmpWarnRuleChannelMapper.selectByRuleId(ruleId);
        if (channelList == null || channelList.size() == 0) {
            logger.info("没有报警通知方式");
//            return;
        }
        // 查找报警通知人
        List<AppMsgToUserDTO> userList = appEmpWarnRuleUserMapper.selectByRuleId(ruleId);
        if (userList == null || userList.size() == 0) {
            logger.info("没有报警通知人");
//            return;
        }

        String title = AppEmpWarnRuleType.parseByValue(ruleType).tittle();
        String ruleStr = AppEmpWarnRuleType.parseByValue(ruleType).detail();
        String timeStr = DateUtil.formatDate(time, "yyyy-MM-dd HH:mm:ss");

        // 插入报警
        for (AppWarnEmpDTO appWarnEmpDTO : objectIds) {
            String empName = appWarnEmpDTO.getEmpName();
            String workTypeName = appWarnEmpDTO.getWorkTypeName();
            Integer empId = appWarnEmpDTO.getEmpId();

            AppEmpWarn appEmpWarn = new AppEmpWarn();
            appEmpWarn.setTriggerObjectId(String.valueOf(empId));
            appEmpWarn.setDeptId(deptId);
            appEmpWarn.setLat(lat);
            appEmpWarn.setLng(lnt);
            appEmpWarn.setRuleId(ruleId);
            appEmpWarn.setRuleType(ruleType);
            appEmpWarn.setTriggerTime(time);
            appEmpWarnMapper.insertSelective(appEmpWarn);

            String content = String.format(EMP_CONTENT, workTypeName + " " + empName, timeStr, ruleStr);

            PushMsgDTO pushMsgDTO = new PushMsgDTO();
            pushMsgDTO.setContent(content);
            pushMsgDTO.setDeptId(deptId);
            pushMsgDTO.setModuleType(AppWarnModuleType.EMP.value());
            pushMsgDTO.setMsgChannelList(channelList);
            pushMsgDTO.setMsgObjectId(String.valueOf(appEmpWarn.getId()));
            pushMsgDTO.setTitle(title);
            pushMsgDTO.setToUserList(userList);
            pushMsgDTO.setTime(time);
            amqpTemplate.convertAndSend(QueueConst.PUSH_MSG_EXCHANGE, "", JSONObject.toJSONString(pushMsgDTO));
        }
    }

    private void generateWarnAndMessage(EmpWarnCheckDTO warnCheckDTO) {
        Integer empId = warnCheckDTO.getEmpId();
        AppEmp appEmp = appEmpMapper.selectByPrimaryKey(empId);
        if (appEmp == null) {
            return;
        }
        Integer deptId = appEmp.getProjectId();
        Date triggerTime = warnCheckDTO.getTriggerTime();
        Double lat = warnCheckDTO.getLat();
        Double lng = warnCheckDTO.getLng();
        String timeStr = DateUtil.formatDate(triggerTime, "yyyy-MM-dd HH:mm:ss");
        List<Integer> ruleTypeList = warnCheckDTO.getRuleTypeList();

        for (Integer ruleType : ruleTypeList) {
            Integer ruleId;
            if (AppEmpWarnRuleType.FENCE.value().equals(ruleType)) {
                ruleId = warnCheckDTO.getRuleId();
            } else {
                ruleId = appEmpWarnRuleMapper.selectByRuleType(ruleType, deptId);
            }

            if (ruleId == null) {
                continue;
            }

            // 检测报警时间
            boolean isMeetWarnTime = this.checkWarnTime(ruleId, triggerTime);
            if (!isMeetWarnTime) {
                logger.info("报警时间未到 isMeetWarnTime:{}",isMeetWarnTime);
//                continue;
            }

            // 检测报警频率
            String objectId = String.valueOf(empId);
            boolean isMeetAlarmFrequency = warnConfigService.checkAlarmFrequency(deptId, AppModuleType.EMP.value(), ruleType, objectId, triggerTime);
            if (!isMeetAlarmFrequency) {
                logger.info("报警时间未到 isMeetAlarmFrequency:{}",isMeetAlarmFrequency);
//                continue;
            }

            // 获取报警规则，检查旁站人员分级推送是否启用
            AppEmpWarnRule warnRule = appEmpWarnRuleMapper.selectByPrimaryKey(ruleId);
            if (warnRule == null) {
                continue;
            }

            String title = AppEmpWarnRuleType.parseByValue(ruleType).tittle();
            String ruleStr = AppEmpWarnRuleType.parseByValue(ruleType).detail();

            AppEmpWarn appEmpWarn = new AppEmpWarn();
            appEmpWarn.setTriggerObjectId(String.valueOf(empId));
            appEmpWarn.setDeptId(deptId);
            appEmpWarn.setLat(lat);
            appEmpWarn.setLng(lng);
            appEmpWarn.setRuleId(ruleId);
            appEmpWarn.setRuleType(ruleType);
            appEmpWarn.setTriggerTime(triggerTime);
            List<OpenApiEmpWarnDTO> warnDTOS = appEmpWarnMapper.selectOpenApiEmpWarnList(empId, triggerTime, triggerTime);
            if (warnDTOS != null && !warnDTOS.isEmpty()){
                logger.info("过滤重复告警数据 empId:{}- time:{}", warnCheckDTO.getEmpId(), warnCheckDTO.getTriggerTime());
                return;
            }
            appEmpWarnMapper.insertSelective(appEmpWarn);

            // 查找报警通知人
            List<AppMsgToUserDTO> userList = appEmpWarnRuleUserMapper.selectByRuleId(ruleId);
            if (userList == null ) {
                userList = new ArrayList<>();
            }

            String extraInfo = warnCheckDTO.getExtraInfo();
            if (StringUtils.isNotBlank(extraInfo)){
                JSONObject workPlan = JSONObject.parseObject(extraInfo);
                Integer planId = workPlan.getInteger("planId");
                JSONObject members = workPlan.getJSONObject("members");
                if (planId != null && ObjectUtils.allNotNull(members)){
                    String guardian1Id = members.getString("guardian1Id");
                    fillMsgToUser(guardian1Id, ruleId, userList);
                    String guardian2Id = members.getString("guardian2Id");
                    fillMsgToUser(guardian2Id, ruleId, userList);
                    // 如果启用了旁站人员分级推送，则添加旁站人员到通知列表
                    if (warnRule.getGuardianFlag() == null || warnRule.getGuardianFlag() != 1) {
                        String guardian3Id = members.getString("guardian3Id");
                        fillMsgToUser(guardian3Id, ruleId, userList);
                        String guardian4Id = members.getString("guardian4Id");
                        fillMsgToUser(guardian4Id, ruleId, userList);
                    }
                }
            }

            if (userList.isEmpty()) {
                continue;
            }

            // 查找报警通知方式
            List<Integer> channelList = appEmpWarnRuleChannelMapper.selectByRuleId(ruleId);
            if (channelList == null || channelList.isEmpty()) {
                continue;
            }

            String content = String.format(EMP_CONTENT, appEmp.getWorkTypeName() + " " + appEmp.getEmpName(), timeStr, ruleStr);
            PushMsgDTO pushMsgDTO = new PushMsgDTO();
            pushMsgDTO.setContent(content);
            pushMsgDTO.setDeptId(deptId);
            pushMsgDTO.setModuleType(AppWarnModuleType.EMP.value());
            pushMsgDTO.setMsgChannelList(channelList);
            pushMsgDTO.setMsgObjectId(String.valueOf(appEmpWarn.getId()));
            pushMsgDTO.setTitle(title);
            pushMsgDTO.setToUserList(userList);
            pushMsgDTO.setTime(triggerTime);
            amqpTemplate.convertAndSend(QueueConst.PUSH_MSG_EXCHANGE, "", JSONObject.toJSONString(pushMsgDTO));
        }
    }

    private void fillMsgToUser(String empIds, Integer ruleId, List<AppMsgToUserDTO> userList) {
        if (StringUtils.isNotBlank(empIds)) {
            List<String> empIdList = Arrays.asList(empIds.split(","));
            List<AppEmp> emps = appEmpMapper.getEmpInfoList(empIdList);
            for (AppEmp emp: emps){
                SysEmpUser sysEmpUser = sysUserService.getEmpUserByEmpId(emp.getId());
                if (sysEmpUser != null){
                    AppMsgToUserDTO appMsgToUserDTO = new AppMsgToUserDTO();
                    appMsgToUserDTO.setDeptId(emp.getDeptId());
                    appMsgToUserDTO.setPhone(emp.getPhone());
                    appMsgToUserDTO.setUsername(emp.getEmpName());
                    appMsgToUserDTO.setUserId(sysEmpUser.getSysUserId());
                    appMsgToUserDTO.setRuleId(ruleId);
                    userList.add(appMsgToUserDTO);
                }
            }
        }
    }

    private boolean checkWarnTime(Integer ruleId, Date time) {
        // 增加时间浮动
        Random r = new Random();
        int intervalMinutes = r.nextInt(60);

        boolean flag = false;
        List<AppWarnTimeDTO> appWarnTimeDTOS = appEmpWarnRuleTimeMapper.selectByRuleId(ruleId);
        for (AppWarnTimeDTO warnTimeDTO : appWarnTimeDTOS) {
            Date startTime = DateUtil.addMinutes(warnTimeDTO.getStartTime(), intervalMinutes);
            Date endTime = warnTimeDTO.getEndTime();
            if (endTime.after(DateUtil.getTime(time)) && startTime.before(DateUtil.getTime(time))) {
                flag = true;
                break;
            }
        }
        if (appWarnTimeDTOS.size() == 0) {
            flag = true;
        }
        return flag;
    }


    /**
     * 检测是否触发电子围栏报警
     *
     * @param warnCheckDTO
     * @return
     */
    private boolean checkFenceWarn(EmpWarnCheckDTO warnCheckDTO) {
        Integer empId = warnCheckDTO.getEmpId();
        Double lat = warnCheckDTO.getLat();
        Double lng = warnCheckDTO.getLng();
        AppFenceDTO fenceDTO = empWarnRuleObjectMapper.selectByEmpId(empId);
        if (fenceDTO == null) {
            return false;
        }
        warnCheckDTO.setRuleId(fenceDTO.getRuleId());
        Integer type = fenceDTO.getType();
        if (FenceType.CIRCLE.value().equals(type)) {
            Double radius = MathUtil.round(fenceDTO.getRadius() / 1000, 5);
            return !GeometryUtil.isInCircle(fenceDTO.getCenter(), radius, lat, lng);
        }
        if (FenceType.POLYGON.value().equals(type)) {
            return !GeometryUtil.isInPolygon(fenceDTO.getPolygon(), lat, lng);
        }
        return false;
    }

}